home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / console.py < prev    next >
Text File  |  2009-08-31  |  29KB  |  862 lines

  1. #!BPY
  2.  
  3. """
  4. Name: 'Interactive Python Console'
  5. Blender: 245
  6. Group: 'System'
  7. Tooltip: 'Interactive Python Console'
  8. """
  9.  
  10. __author__ = "Campbell Barton aka ideasman42"
  11. __url__ = ["www.blender.org", "blenderartists.org", "www.python.org"]
  12. __bpydoc__ = """\
  13. This is an interactive console, similar to Python's own command line interpreter.  Since it is embedded in Blender, it has access to all Blender Python modules.
  14.  
  15. Those completely new to Python are recommended to check the link button above
  16. that points to its official homepage, with news, downloads and documentation.
  17.  
  18. Usage:<br>
  19.   Type your code and hit "Enter" to get it executed.<br>
  20.   - Right mouse click: Console Menu (Save output, etc);<br>
  21.   - Mousewheel: Scroll text
  22.   - Arrow keys: command history and cursor;<br>
  23.   - Shift + Backspace: Backspace whole word;<br>
  24.   - Shift + Arrow keys: jump words;<br>
  25.   - Ctrl + (+/- or mousewheel): Zoom text size;<br>
  26.   - Ctrl + Enter: auto compleate based on variable names and modules loaded -- multiple choices popup a menu;<br>
  27.   - Shift + Enter: multiline functions -- delays executing code until only Enter is pressed.
  28. """
  29.  
  30. # -------------------------------------------------------------------------- 
  31. # ***** BEGIN GPL LICENSE BLOCK ***** 
  32. # This program is free software; you can redistribute it and/or 
  33. # modify it under the terms of the GNU General Public License 
  34. # as published by the Free Software Foundation; either version 2 
  35. # of the License, or (at your option) any later version. 
  36. # This program is distributed in the hope that it will be useful, 
  37. # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  38. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the 
  39. # GNU General Public License for more details. 
  40. # You should have received a copy of the GNU General Public License 
  41. # along with this program; if not, write to the Free Software Foundation, 
  42. # Inc., 59 Temple Place - Suite 330, Boston, MA    02111-1307, USA. 
  43. # ***** END GPL LICENCE BLOCK ***** 
  44. # -------------------------------------------------------------------------- 
  45.  
  46. import Blender
  47. import bpy
  48. from Blender import *
  49. import sys as python_sys
  50. import StringIO
  51.  
  52. # Constants
  53. __DELIMETERS__ = '. ,=+-*/%<>&~][{}():\t'
  54. __VARIABLE_DELIMETERS__ = ' ,=+-*/%<>&~{}():\t'
  55.  
  56. __LINE_HISTORY__ = 500
  57.  
  58. global __FONT_SIZE__
  59.  
  60. __FONT_SIZES__ = ( ('tiny', 10), ('small', 12), ('normalfix', 14), ('large', 16) )
  61. __FONT_SIZE__ = 2 # index for the list above, normal default.
  62.  
  63. global __CONSOLE_LINE_OFFSET__
  64. __CONSOLE_LINE_OFFSET__ = 0
  65.  
  66. cmdBuffer = [] # dosnt need to be global
  67.  
  68. '''
  69. # Generic Blender functions
  70. def getActScriptWinRect():
  71.     area = Window.GetAreaSize()
  72.     area = (area[0]-1, area[1]-1)
  73.     for scrInfo in Window.GetScreenInfo(Window.Types['SCRIPT'], 'win', ''):
  74.         if scrInfo['vertices'][2]-scrInfo['vertices'][0] == area[0]:
  75.             if scrInfo['vertices'][3]-scrInfo['vertices'][1] == area[1]:
  76.                 return scrInfo['vertices']
  77.     return None
  78. '''
  79.  
  80.  
  81. # menuText, # per group
  82. def PupMenuLess(menu, groupSize=35):
  83.     more = ['   more...']
  84.     less = ['   less...']
  85.     
  86.     menuList= menu.split('|')
  87.     
  88.     # No Less Needed, just call.
  89.     if len(menuList) < groupSize:
  90.         return Draw.PupMenu(menu)
  91.     
  92.     title = menuList[0].split('%t')[0]
  93.     
  94.     # Split the list into groups
  95.     menuGroups = [[]]
  96.     for li in menuList[1:]:
  97.         if len(menuGroups[-1]) < groupSize:
  98.             menuGroups[-1].append(li)
  99.         else:
  100.             menuGroups.append([li])
  101.     
  102.     # Stores teh current menu group we are looking at
  103.     groupIdx = 0
  104.     while 1:
  105.         # Give us a title with the menu number
  106.         numTitle = [ ' '.join([title, str(groupIdx + 1), 'of', str(len(menuGroups)), '%t'])]
  107.         if groupIdx == 0:
  108.             menuString = '|'.join(numTitle + menuGroups[groupIdx] + more)
  109.         elif groupIdx == len(menuGroups)-1:
  110.             menuString = '|'.join(numTitle + less + menuGroups[groupIdx])
  111.         else: # In the middle somewhere so Show a more and less
  112.             menuString = '|'.join(numTitle + less + menuGroups[groupIdx] + more)
  113.         result = Draw.PupMenu(menuString)
  114.         # User Exit
  115.         if result == -1:
  116.             return -1
  117.         
  118.         if groupIdx == 0: # First menu
  119.             if result-1 < groupSize:
  120.                 return result
  121.             else: # must be more
  122.                 groupIdx +=1
  123.         elif groupIdx == len(menuGroups): # Last Menu
  124.             if result == 1: # Must be less
  125.                 groupIdx -= 1
  126.             else: # Must be a choice
  127.                 return result + (groupIdx*groupSize)
  128.             
  129.         else:    
  130.             if result == 1: # Must be less
  131.                 groupIdx -= 1
  132.             elif result-2 == groupSize:
  133.                 groupIdx +=1
  134.             else:
  135.                 return result - 1 + (groupIdx*groupSize)
  136.                 
  137.  
  138.  
  139. # Use newstyle classes, Im not bothering with inheretence
  140. # but slots are faster.
  141. class cmdLine(object):
  142.     __slots__ = [\
  143.     'cmd', # is the command string, or any other message
  144.     'type',# type: 0:user input  1:program feedback  2:error message.  3:option feedback
  145.     'exe' #  0- not yet executed   1:executed
  146.     ]
  147.     def __init__(self, cmd, type, exe):
  148.         self.cmd = cmd
  149.         self.type = type
  150.         self.exe = exe
  151.  
  152. # Include external file with internal namespace
  153. def include(filename):
  154.     file = open(filename, 'r')
  155.     filedata = file.read()
  156.     file.close()
  157.     return compile(filedata, filename, 'exec')
  158.  
  159. # Writes command line data to a blender text file.
  160. def writeCmdData(type):
  161.     newText = Text.New('command_output.py', 1)
  162.     if type == 3:    newText.write('\n'.join( [ myCmd.cmd for myCmd in cmdBuffer ] ))
  163.     else:            newText.write('\n'.join( [ myCmd.cmd for myCmd in cmdBuffer if myCmd.type is type] ))
  164.     Draw.PupMenu('%s written' % newText.name)
  165.  
  166. def insertCmdData():
  167.     texts = list(bpy.data.texts)
  168.     textNames = [tex.name for tex in texts]
  169.     if textNames:
  170.         choice = Draw.PupMenu('|'.join(textNames))
  171.         if choice != -1:
  172.             text = texts[choice-1]
  173.             
  174.             # Add the text!
  175.             for l in text.asLines():
  176.                 cmdBuffer.append(cmdLine('%s ' % l, 0, 0))
  177.             Draw.Redraw()
  178.     
  179.  
  180. COLLECTED_VAR_NAMES = {} # a list of keys, each key has a list of absolute paths
  181.  
  182. # Pain and simple recursice dir(), accepts a string
  183. unused_types = str, dict, list, float, int, str, type, tuple, type(dir), type(None)
  184. def rdir(dirString, depth=0):
  185.     #print ' ' * depth, dirString
  186.     # MAX DEPTH SET HERE
  187.     if depth > 5:
  188.         # print 'maxdepoth reached.'
  189.         return
  190.         
  191.     global COLLECTED_VAR_NAMES
  192.     dirStringSplit = dirString.split('.')
  193.     
  194.     exec('value=' + dirString)    
  195.     
  196.     if type(value) in unused_types:
  197.         # print 'bad type'
  198.         return
  199.     dirList = dir(value)
  200.     
  201.     for dirItem in dirList:
  202.         if dirItem.startswith('_'):
  203.             continue
  204.             
  205.         dirData = None
  206.         try:
  207.             # Rare cases this can mess up, material.shader was a problem.
  208.             exec('dirData = %s.%s' % (dirString, dirItem))
  209.             #print dirData
  210.         except:
  211.             # Dont bother with this data.
  212.             continue
  213.         #print  'HEY', dirData, dirItem
  214.         #if type(dirItem) != str:
  215.         #    print dirItem, type(dirItem)
  216.         
  217.         if dirItem not in COLLECTED_VAR_NAMES: # .keys()
  218.             COLLECTED_VAR_NAMES[dirItem] = []
  219.         
  220.         # Add the string
  221.         # splitD = dirString.split('"')[-2]
  222.         
  223.         # Example of dirString
  224.         # __CONSOLE_VAR_DICT__["Main"].scenes.active.render
  225.         
  226.         # Works but can be faster
  227.         # splitD = dirString.replace('__CONSOLE_VAR_DICT__["', '').replace('"]', '')
  228.         
  229.         splitD = dirString[22:].replace('"]', '')
  230.         
  231.         if splitD not in COLLECTED_VAR_NAMES[dirItem]:
  232.             # print dirItem, dirString, splitD,
  233.             COLLECTED_VAR_NAMES[dirItem].append(splitD)
  234.         
  235.         
  236.         # Stops recursice stuff, overlooping
  237.         #print type(dirItem)
  238.         #if type(dirData) == types.ClassType or \
  239.         #     type(dirData) == types.ModuleType:
  240.         type_dirData = type(dirData)
  241.         if type_dirData not in unused_types:
  242.             # print type(dirData), dirItem
  243.             # Dont loop up dirs for strings ints etc.
  244.             if dirItem not in dirStringSplit:
  245.                 rdir( '%s.%s' % (dirString, dirItem), depth+1)
  246.         '''
  247.         elif depth == 0: # Add local variables
  248.             # print type(dirData), dirItem
  249.             # Dont loop up dirs for strings ints etc.
  250.             if dirItem not in dirStringSplit:
  251.                 rdir( '%s.%s' % (dirString, dirItem), depth+1)
  252.         '''
  253.  
  254. def recursive_dir():
  255.     global COLLECTED_VAR_NAMES
  256.     
  257.     for name in __CONSOLE_VAR_DICT__: # .keys()
  258.         if not name.startswith('_'): # Dont pick names like __name__
  259.             rdir('__CONSOLE_VAR_DICT__["%s"]' % name)
  260.             #print COLLECTED_VAR_NAMES
  261.             COLLECTED_VAR_NAMES[name] = [''] 
  262.     return COLLECTED_VAR_NAMES
  263.  
  264. # Runs the code line(s) the user has entered and handle errors
  265. # As well as feeding back the output into the blender window.
  266. def runUserCode(__USER_CODE_STRING__):
  267.     global __CONSOLE_VAR_DICT__ # We manipulate the variables here. loading and saving from localspace to this global var.
  268.     
  269.     # Open A File like object to write all output to, that would useually be printed. 
  270.     python_sys.stdout.flush() # Get rid of whatever came before
  271.     __FILE_LIKE_STRING__ = StringIO.StringIO() # make a new file like string, this saves up from making a file.
  272.     __STD_OUTPUT__ = python_sys.stdout # we need to store the normal output.
  273.     python_sys.stdout=__FILE_LIKE_STRING__ # Now anything printed will be written to the file like string.
  274.     
  275.     # Try and run the user entered line(s)
  276.     try:
  277.         # Load all variabls from global dict to local space.
  278.         __TMP_VAR_NAME__ = __TMP_VAR__ = '' # so as not to raise an error when del'ing
  279.  
  280.         for __TMP_VAR_NAME__, __TMP_VAR__ in __CONSOLE_VAR_DICT__.items():
  281.             exec('%s%s' % (__TMP_VAR_NAME__,'=__TMP_VAR__'))
  282.         del __TMP_VAR_NAME__
  283.         del __TMP_VAR__
  284.         
  285.         # Now all the vars are loaded, execute the code. # Newline thanks to phillip,
  286.         exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'single')) #exec(compile(__USER_CODE_STRING__, 'blender_cmd.py', 'exec'))
  287.         
  288.         # Flush global dict, allow the user to remove items.
  289.         __CONSOLE_VAR_DICT__ = {}
  290.  
  291.         __TMP_VAR_NAME__ = '' # so as not to raise an error when del'ing    
  292.         # Write local veriables to global __CONSOLE_VAR_DICT__
  293.         for __TMP_VAR_NAME__ in dir():
  294.             if    __TMP_VAR_NAME__ != '__FILE_LIKE_STRING__' and\
  295.                     __TMP_VAR_NAME__ != '__STD_OUTPUT__' and\
  296.                     __TMP_VAR_NAME__ != '__TMP_VAR_NAME__' and\
  297.                     __TMP_VAR_NAME__ != '__USER_CODE_STRING__':
  298.                 
  299.                 # Execute the local > global coversion.
  300.                 exec('%s%s' % ('__CONSOLE_VAR_DICT__[__TMP_VAR_NAME__]=', __TMP_VAR_NAME__))
  301.         del __TMP_VAR_NAME__
  302.     
  303.     except: # Prints the REAL exception.
  304.         error = '%s:  %s' % (python_sys.exc_type, python_sys.exc_value)        
  305.         for errorLine in error.split('\n'):
  306.             cmdBuffer.append(cmdLine(errorLine, 2, None)) # new line to type into
  307.     
  308.     python_sys.stdout = __STD_OUTPUT__ # Go back to output to the normal blender console
  309.     
  310.     # Copy all new output to cmdBuffer
  311.     
  312.     __FILE_LIKE_STRING__.seek(0) # the readline function requires that we go back to the start of the file.
  313.     
  314.     for line in __FILE_LIKE_STRING__.readlines():
  315.         cmdBuffer.append(cmdLine(line, 1, None))
  316.         
  317.     cmdBuffer.append(cmdLine(' ', 0, 0)) # new line to type into
  318.     python_sys.stdout=__STD_OUTPUT__
  319.     __FILE_LIKE_STRING__.close()
  320.  
  321.  
  322.  
  323.  
  324.  
  325. #------------------------------------------------------------------------------#
  326. #                             event handling code                              #
  327. #------------------------------------------------------------------------------#
  328. def handle_event(evt, val):
  329.     
  330.     # Insert Char into the cammand line
  331.     def insCh(ch): # Instert a char
  332.         global cursor
  333.         # Later account for a cursor variable
  334.         cmdBuffer[-1].cmd = ('%s%s%s' % ( cmdBuffer[-1].cmd[:cursor], ch, cmdBuffer[-1].cmd[cursor:]))
  335.     
  336.     #------------------------------------------------------------------------------#
  337.     #                        Define Complex Key Actions                            #
  338.     #------------------------------------------------------------------------------#
  339.     def actionEnterKey():
  340.         global histIndex, cursor
  341.         
  342.         def getIndent(string):
  343.             # Gather white space to add in the previous line
  344.             # Ignore the last char since its padding.
  345.             whiteSpace = ''
  346.             #for i in range(len(cmdBuffer[-1].cmd)):
  347.             for i in xrange(len(string)-1):
  348.                 if cmdBuffer[-1].cmd[i] == ' ' or cmdBuffer[-1].cmd[i] == '\t':
  349.                     whiteSpace += string[i]
  350.                 else:
  351.                     break
  352.             return whiteSpace
  353.         
  354.         # Autocomplete
  355.         if Window.GetKeyQualifiers() & Window.Qual.CTRL:
  356.             actionAutoCompleate()
  357.             return
  358.         
  359.         # Are we in the middle of a multiline part or not?
  360.         # try be smart about it
  361.         if cmdBuffer[-1].cmd.split('#')[0].rstrip().endswith(':'):
  362.             # : indicates an indent is needed
  363.             cmdBuffer.append(cmdLine('\t%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
  364.             print ': indicates an indent is needed'        
  365.         
  366.         elif cmdBuffer[-1].cmd[0] in [' ', '\t'] and len(cmdBuffer[-1].cmd) > 1 and cmdBuffer[-1].cmd.split():
  367.             # white space at the start means he havnt finished the multiline.
  368.             cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
  369.             print 'white space at the start means he havnt finished the multiline.'
  370.         
  371.         elif Window.GetKeyQualifiers() & Window.Qual.SHIFT:
  372.             # Crtl forces multiline
  373.             cmdBuffer.append(cmdLine('%s ' % getIndent(cmdBuffer[-1].cmd), 0, 0))
  374.             print 'Crtl forces multiline'            
  375.         
  376.         else: # Execute multiline code block
  377.             
  378.             # Multiline code will still run with 1 line,
  379.             multiLineCode = ['if 1:'] # End of the multiline first.
  380.             
  381.             # Seek the start of the file multiline
  382.             i = 1
  383.             while cmdBuffer[-i].exe == 0:
  384.                 i+=1
  385.             
  386.             while i > 1:
  387.                 i-=1
  388.                 
  389.                 if cmdBuffer[-i].cmd == ' ':# Tag as an output type so its not used in the key history
  390.                     cmdBuffer[-i].type = 1
  391.                 else: # Tab added at the start for added if 1: statement
  392.                     multiLineCode.append('\t%s' % cmdBuffer[-i].cmd )
  393.                 
  394.                 # Mark as executed
  395.                 cmdBuffer[-i].exe = 1                
  396.                 
  397.             multiLineCode.append('\tpass') # reverse will make this the start.            
  398.             
  399.             # Dubug, print the code that is executed.
  400.             #for m in multiLineCode: print m
  401.             
  402.             runUserCode('\n'.join(multiLineCode))
  403.             
  404.             # Clear the output based on __LINE_HISTORY__
  405.             if len(cmdBuffer) > __LINE_HISTORY__:
  406.                 cmdBuffer[:__LINE_HISTORY__] = []
  407.         
  408.         histIndex = cursor = -1 # Reset cursor and history
  409.     
  410.     def actionUpKey():
  411.         global histIndex
  412.         if abs(histIndex)+1 >= len(cmdBuffer):
  413.             histIndex = -1
  414.             
  415.             # When wrapping allow 1 plank lines
  416.             if cmdBuffer[-1].cmd != ' ':
  417.                 cmdBuffer[-1].cmd = ' '
  418.                 return
  419.         
  420.         histIndex_orig = histIndex
  421.         histIndex -= 1
  422.         
  423.         while    (cmdBuffer[histIndex].type != 0 and abs(histIndex) < len(cmdBuffer)) or \
  424.                 ( cmdBuffer[histIndex].cmd == cmdBuffer[histIndex_orig].cmd):
  425.             histIndex -= 1
  426.             
  427.         if cmdBuffer[histIndex].type == 0: # we found one
  428.             cmdBuffer[-1].cmd = cmdBuffer[histIndex].cmd            
  429.     
  430.     def actionDownKey():
  431.         global histIndex
  432.         if histIndex >= -2:
  433.             histIndex = -len(cmdBuffer)
  434.             
  435.             # When wrapping allow 1 plank lines
  436.             if cmdBuffer[-1].cmd != ' ':
  437.                 cmdBuffer[-1].cmd = ' '
  438.                 return
  439.             
  440.         histIndex_orig = histIndex
  441.         histIndex += 1
  442.         while    (cmdBuffer[histIndex].type != 0 and histIndex != -2) or \
  443.                 ( cmdBuffer[histIndex].cmd == cmdBuffer[histIndex_orig].cmd):
  444.             
  445.             histIndex += 1
  446.             
  447.         if cmdBuffer[histIndex].type == 0: # we found one
  448.             cmdBuffer[-1].cmd = cmdBuffer[histIndex].cmd
  449.     
  450.     def actionRightMouse():
  451.         global __FONT_SIZE__
  452.         choice = Draw.PupMenu('Console Menu%t|Write Input Data (white)|Write Output Data (blue)|Write Error Data (red)|Write All Text|%l|Insert Blender text|%l|Font Size|%l|Clear Output|Quit')
  453.         
  454.         if choice == 1:
  455.             writeCmdData(0) # type 0 user
  456.         elif choice == 2:
  457.             writeCmdData(1) # type 1 user output
  458.         elif choice == 3:
  459.             writeCmdData(2) # type 2 errors
  460.         elif choice == 4:
  461.             writeCmdData(3) # All
  462.         elif choice == 6:
  463.             insertCmdData() # Insert text from Blender and run it.
  464.         elif choice == 8:
  465.             # Fontsize.
  466.             font_choice = Draw.PupMenu('Font Size%t|Large|Normal|Small|Tiny')
  467.             if font_choice != -1:
  468.                 if font_choice == 1:
  469.                     __FONT_SIZE__ = 3
  470.                 elif font_choice == 2:
  471.                     __FONT_SIZE__ = 2
  472.                 elif font_choice == 3:
  473.                     __FONT_SIZE__ = 1
  474.                 elif font_choice == 4:
  475.                     __FONT_SIZE__ = 0
  476.                 Draw.Redraw()
  477.         elif choice == 10: # Clear all output
  478.             cmdBuffer[:] = [cmd for cmd in cmdBuffer if cmd.type == 0] # keep user input
  479.             Draw.Redraw()
  480.         elif choice == 11: # Exit
  481.             Draw.Exit()
  482.     
  483.     
  484.     # Auto compleating, quite complex- use recutsice dir for the moment.
  485.     def actionAutoCompleate(): # Ctrl + Tab
  486.         if not cmdBuffer[-1].cmd[:cursor].split():
  487.             return
  488.         
  489.         
  490.         RECURSIVE_DIR = recursive_dir()
  491.         
  492.         # get last name of user input
  493.         editVar = cmdBuffer[-1].cmd[:cursor]
  494.         # Split off spaces operators etc from the staryt of the command so we can use the startswith function.
  495.         for splitChar in __VARIABLE_DELIMETERS__:
  496.             editVar = editVar[:-1].split(splitChar)[-1] + editVar[-1]
  497.         
  498.         
  499.         # Now we should have the var by its self
  500.         if editVar:
  501.             possibilities = []
  502.             
  503.             for __TMP_VAR_NAME__ in RECURSIVE_DIR: #.keys():
  504.                 #print '\t', __TMP_VAR_NAME__
  505.                 if __TMP_VAR_NAME__ == editVar:
  506.                     # print 'ADITVAR IS A VAR'
  507.                     pass
  508.                 '''
  509.                 elif __TMP_VAR_NAME__.startswith( editVar ):
  510.                     print __TMP_VAR_NAME__, 'aaa'
  511.                     possibilities.append( __TMP_VAR_NAME__ )
  512.                 '''
  513.                 possibilities.append( __TMP_VAR_NAME__ )
  514.             
  515.             if len(possibilities) == 1:
  516.                 cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], possibilities[0], cmdBuffer[-1].cmd[cursor:]))    
  517.             
  518.             elif possibilities: # If its not just []
  519.                 # -1 with insert is the second last.
  520.                 
  521.                 # Text choice
  522.                 #cmdBuffer.insert(-1, cmdLine('options: %s' % ' '.join(possibilities), 3, None))
  523.                 
  524.                 menuList = [] # A lits of tuples- ABSOLUTE, RELATIVE
  525.                 
  526.                 for __TMP_VAR_NAME__ in possibilities:
  527.                     for usage in RECURSIVE_DIR[__TMP_VAR_NAME__]:
  528.                         # Account for non absolute (variables for eg.)
  529.                         if usage: # not ''
  530.                             absName = '%s.%s' % (usage, __TMP_VAR_NAME__)
  531.                             
  532.                             if __TMP_VAR_NAME__.startswith(editVar):
  533.                                 menuList.append( # Used for names and can be entered when pressing shift.
  534.                                   (absName, # Absolute name
  535.                                   __TMP_VAR_NAME__) # Relative name, non shift
  536.                                   )
  537.                         
  538.                         #else:
  539.                         #    if absName.find(editVar) != -1:
  540.                         #        menuList.append((__TMP_VAR_NAME__, __TMP_VAR_NAME__)) # Used for names and can be entered when pressing shift.
  541.                 
  542.                 # No items to display? no menu
  543.                 if not menuList:
  544.                     return
  545.                     
  546.                 menuList.sort()
  547.                 
  548.                 choice = PupMenuLess( # Menu for the user to choose the autocompleate
  549.                 'Choices (Shift for local name, Ctrl for Docs)%t|' + # Title Text
  550.                 '|'.join(['%s,  %s' % m for m in menuList])) # Use Absolute names m[0]
  551.                 
  552.                 if choice != -1:
  553.                     if Window.GetKeyQualifiers() & Window.Qual.CTRL:  # Help
  554.                         cmdBuffer[-1].cmd = ('help(%s%s) ' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][0]))    
  555.                     elif Window.GetKeyQualifiers() & Window.Qual.SHIFT:  # Put in the long name
  556.                         cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][1], cmdBuffer[-1].cmd[cursor:]))    
  557.                     else: # Only paste in the Short name
  558.                         cmdBuffer[-1].cmd = ('%s%s%s' % (cmdBuffer[-1].cmd[:cursor - len(editVar)], menuList[choice-1][0], cmdBuffer[-1].cmd[cursor:]))    
  559.                         
  560.                         
  561.         else:
  562.             # print 'NO EDITVAR'
  563.             return
  564.         
  565.     # ------------------end------------------ #
  566.     
  567.     # Quit from menu only
  568.     #if (evt == Draw.ESCKEY and not val):
  569.     #    Draw.Exit()
  570.     if evt == Draw.MOUSEX or evt == Draw.MOUSEY: # AVOID TOO MANY REDRAWS.
  571.         return    
  572.     
  573.     
  574.     global cursor
  575.     global histIndex    
  576.     global __FONT_SIZE__
  577.     global __CONSOLE_LINE_OFFSET__
  578.     
  579.     ascii = Blender.event
  580.     
  581.     resetScroll = True
  582.     
  583.     #------------------------------------------------------------------------------#
  584.     #                             key codes and key handling                       #
  585.     #------------------------------------------------------------------------------#
  586.     
  587.     # UP DOWN ARROW KEYS, TO TRAVERSE HISTORY
  588.     if (evt == Draw.UPARROWKEY and val): actionUpKey()
  589.     elif (evt == Draw.DOWNARROWKEY and val): actionDownKey()
  590.     
  591.     elif (evt == Draw.RIGHTARROWKEY and val):
  592.         if Window.GetKeyQualifiers() & Window.Qual.SHIFT:
  593.             wordJump = False
  594.             newCursor = cursor+1
  595.             while newCursor<0:
  596.                 
  597.                 if cmdBuffer[-1].cmd[newCursor] not in __DELIMETERS__:
  598.                     newCursor+=1
  599.                 else:
  600.                     wordJump = True
  601.                     break
  602.             if wordJump: # Did we find a new cursor pos?
  603.                 cursor = newCursor
  604.             else:
  605.                 cursor = -1 # end of line
  606.         else:
  607.             cursor +=1
  608.             if cursor > -1:
  609.                 cursor = -1
  610.     
  611.     elif (evt == Draw.LEFTARROWKEY and val):
  612.         if Window.GetKeyQualifiers() & Window.Qual.SHIFT:
  613.             wordJump = False
  614.             newCursor = cursor-1
  615.             while abs(newCursor) < len(cmdBuffer[-1].cmd):
  616.                 
  617.                 if cmdBuffer[-1].cmd[newCursor] not in __DELIMETERS__ or\
  618.                 newCursor == cursor:
  619.                     newCursor-=1
  620.                 else:
  621.                     wordJump = True
  622.                     break
  623.             if wordJump: # Did we find a new cursor pos?
  624.                 cursor = newCursor
  625.             else: 
  626.                 cursor = -len(cmdBuffer[-1].cmd) # Start of line
  627.             
  628.         else:
  629.             if len(cmdBuffer[-1].cmd) > abs(cursor):
  630.                 cursor -=1
  631.     
  632.     elif (evt == Draw.HOMEKEY and val):
  633.         cursor  = -len(cmdBuffer[-1].cmd)
  634.     
  635.     elif (evt == Draw.ENDKEY and val):
  636.         cursor = -1
  637.     
  638.     elif (evt == Draw.TABKEY and val):
  639.         insCh('\t')    
  640.     
  641.     elif (evt == Draw.BACKSPACEKEY and val):
  642.         if Window.GetKeyQualifiers() & Window.Qual.SHIFT:
  643.             i = -1
  644.             for d in __DELIMETERS__:
  645.                 i = max(i, cmdBuffer[-1].cmd[:cursor-1].rfind(d))
  646.             if i == -1:
  647.                 i=0
  648.             cmdBuffer[-1].cmd = ('%s%s' % (cmdBuffer[-1].cmd[:i] , cmdBuffer[-1].cmd[cursor:]))
  649.             
  650.         else:
  651.             # Normal backspace.
  652.             cmdBuffer[-1].cmd = ('%s%s' % (cmdBuffer[-1].cmd[:cursor-1] , cmdBuffer[-1].cmd[cursor:]))
  653.             
  654.     elif (evt == Draw.DELKEY and val) and cursor < -1:
  655.         cmdBuffer[-1].cmd = cmdBuffer[-1].cmd[:cursor] + cmdBuffer[-1].cmd[cursor+1:]
  656.         cursor +=1
  657.     
  658.     elif ((evt == Draw.RETKEY or evt == Draw.PADENTER) and val):
  659.         actionEnterKey()
  660.             
  661.     elif (evt == Draw.RIGHTMOUSE and not val): actionRightMouse(); return
  662.     
  663.     elif (evt == Draw.PADPLUSKEY or evt == Draw.EQUALKEY or evt == Draw.WHEELUPMOUSE) and val and Window.GetKeyQualifiers() & Window.Qual.CTRL:
  664.         __FONT_SIZE__ += 1
  665.         __FONT_SIZE__ = min(len(__FONT_SIZES__)-1, __FONT_SIZE__)
  666.     elif (evt == Draw.PADMINUS or evt == Draw.MINUSKEY or evt == Draw.WHEELDOWNMOUSE) and val and Window.GetKeyQualifiers() & Window.Qual.CTRL:
  667.         __FONT_SIZE__ -=1
  668.         __FONT_SIZE__ = max(0, __FONT_SIZE__)
  669.     
  670.     
  671.     elif evt == Draw.WHEELUPMOUSE and val:
  672.         __CONSOLE_LINE_OFFSET__ += 1
  673.         __CONSOLE_LINE_OFFSET__ = min(len(cmdBuffer)-2, __CONSOLE_LINE_OFFSET__)
  674.         resetScroll = False
  675.         
  676.     elif evt == Draw.WHEELDOWNMOUSE and val:
  677.         __CONSOLE_LINE_OFFSET__ -= 1
  678.         __CONSOLE_LINE_OFFSET__ = max(0, __CONSOLE_LINE_OFFSET__)
  679.         resetScroll = False
  680.     
  681.  
  682.     elif ascii:
  683.         insCh(chr(ascii))
  684.     else:
  685.         return # dont redraw.
  686.     
  687.     # If the user types in anything then scroll to bottom.
  688.     if resetScroll:
  689.         __CONSOLE_LINE_OFFSET__ = 0
  690.     Draw.Redraw()
  691.  
  692.  
  693. def draw_gui():
  694.     # Get the bounds from ObleGL directly
  695.     __CONSOLE_RECT__ = BGL.Buffer(BGL.GL_FLOAT, 4)
  696.     BGL.glGetFloatv(BGL.GL_SCISSOR_BOX, __CONSOLE_RECT__) 
  697.     __CONSOLE_RECT__= __CONSOLE_RECT__.list
  698.     
  699.     # Clear the screen
  700.     BGL.glClearColor(0.0, 0.0, 0.0, 1.0)
  701.     BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)         # use it to clear the color buffer
  702.     
  703.     
  704.     # Fixed margin. use a margin since 0 margin can be hard to seewhen close to a crt's edge.
  705.     margin = 4
  706.     
  707.     # Convenience
  708.     FNT_NAME, FNT_HEIGHT = __FONT_SIZES__[__FONT_SIZE__]
  709.     
  710.     # Draw cursor location colour
  711.     if __CONSOLE_LINE_OFFSET__ == 0:
  712.         cmd2curWidth = Draw.GetStringWidth(cmdBuffer[-1].cmd[:cursor], FNT_NAME)
  713.         BGL.glColor3f(0.8, 0.2, 0.2)
  714.         if cmd2curWidth == 0:
  715.             BGL.glRecti(margin,2,margin+2, FNT_HEIGHT+2)
  716.         else:
  717.             BGL.glRecti(margin + cmd2curWidth-2,2, margin+cmd2curWidth, FNT_HEIGHT+2)
  718.     
  719.     BGL.glColor3f(1,1,1)
  720.     # Draw the set of cammands to the buffer
  721.     consoleLineIdx = __CONSOLE_LINE_OFFSET__ + 1
  722.     wrapLineIndex = 0
  723.     while consoleLineIdx < len(cmdBuffer) and  __CONSOLE_RECT__[3] > (consoleLineIdx - __CONSOLE_LINE_OFFSET__) * FNT_HEIGHT:
  724.         if cmdBuffer[-consoleLineIdx].type == 0:
  725.             BGL.glColor3f(1, 1, 1)
  726.         elif cmdBuffer[-consoleLineIdx].type == 1:
  727.             BGL.glColor3f(.3, .3, 1)
  728.         elif cmdBuffer[-consoleLineIdx].type == 2:
  729.             BGL.glColor3f(1.0, 0, 0)
  730.         elif cmdBuffer[-consoleLineIdx].type == 3:
  731.             BGL.glColor3f(0, 0.8, 0)
  732.         else:  
  733.             BGL.glColor3f(1, 1, 0)
  734.         
  735.         if consoleLineIdx == 1: # user input
  736.             BGL.glRasterPos2i(margin, (FNT_HEIGHT * (consoleLineIdx-__CONSOLE_LINE_OFFSET__)) - 8)
  737.             Draw.Text(cmdBuffer[-consoleLineIdx].cmd, FNT_NAME)        
  738.         else: # WRAP
  739.             lwid = Draw.GetStringWidth(cmdBuffer[-consoleLineIdx].cmd, FNT_NAME)
  740.             if margin + lwid >  __CONSOLE_RECT__[2]:
  741.                 wrapLineList = []
  742.                 wtext = cmdBuffer[-consoleLineIdx].cmd
  743.                 wlimit = len(wtext)
  744.                 chunksz = int(( __CONSOLE_RECT__[2] - margin ) / (lwid / len(wtext)))
  745.                 lstart = 0
  746.                 fsize = FNT_NAME
  747.                 while lstart < wlimit:
  748.                     lend = min(lstart+chunksz,wlimit)
  749.                     ttext = wtext[lstart:lend]
  750.                     while lend < wlimit and Draw.GetStringWidth(ttext, fsize) + margin < __CONSOLE_RECT__[2]:
  751.                         lend += 1
  752.                         ttext = wtext[lstart:lend]
  753.                     while lend > lstart+1 and Draw.GetStringWidth(ttext, fsize) + margin > __CONSOLE_RECT__[2]:
  754.                         lend -= 1
  755.                         ttext = wtext[lstart:lend]
  756.                     wrapLineList.append(ttext)
  757.                     lstart = lend 
  758.                 # Now we have a list of lines, draw them (OpenGLs reverse ordering requires this odd change)
  759.                 wrapLineList.reverse()
  760.                 for wline in wrapLineList:
  761.                     BGL.glRasterPos2i(margin, (FNT_HEIGHT*((consoleLineIdx-__CONSOLE_LINE_OFFSET__) + wrapLineIndex)) - 8)
  762.                     Draw.Text(wline, FNT_NAME)
  763.                     wrapLineIndex += 1
  764.                 wrapLineIndex-=1 # otherwise we get a silly extra line.    
  765.                 
  766.             else: # no wrapping.
  767.                 
  768.                 BGL.glRasterPos2i(margin, (FNT_HEIGHT * ((consoleLineIdx-__CONSOLE_LINE_OFFSET__)+wrapLineIndex)) - 8)
  769.                 Draw.Text(cmdBuffer[-consoleLineIdx].cmd, FNT_NAME)
  770.         consoleLineIdx += 1
  771.  
  772. # This recieves the event index, call a function from here depending on the event.
  773. def handle_button_event(evt):
  774.     pass
  775.  
  776.  
  777. # Run the console
  778. __CONSOLE_VAR_DICT__ = {} # Initialize var dict
  779.  
  780.  
  781. # Print Startup lines, add __bpydoc__ to the console startup.
  782. for l in __bpydoc__.split('<br>'):
  783.     cmdBuffer.append( cmdLine(l, 1, None) )
  784.     
  785.  
  786. histIndex = cursor = -1 # How far back from the first letter are we? - in current CMD line, history if for moving up and down lines.
  787.  
  788. # Autoexec, startup code.
  789. scriptDir = Get('scriptsdir')
  790. console_autoexec = None
  791. if scriptDir:
  792.     if not scriptDir.endswith(Blender.sys.sep):
  793.         scriptDir += Blender.sys.sep
  794.     
  795.     console_autoexec  = '%s%s' % (scriptDir, 'console_autoexec.py')
  796.  
  797.     if not sys.exists(console_autoexec):
  798.         # touch the file
  799.         try:
  800.             open(console_autoexec, 'w').close()
  801.             cmdBuffer.append(cmdLine('...console_autoexec.py not found, making new in scripts dir', 1, None))
  802.         except:
  803.             cmdBuffer.append(cmdLine('...console_autoexec.py could not write, this is ok', 1, None))
  804.             scriptDir = None # make sure we only use this for console_autoexec.py
  805.     
  806.     if not sys.exists(console_autoexec):
  807.         console_autoexec = None
  808.     
  809.     else:
  810.         cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts dir', 1, None))
  811.  
  812.  
  813.  
  814. #-Autoexec---------------------------------------------------------------------#
  815. # Just use the function to jump into local naming mode.
  816. # This is so we can loop through all of the autoexec functions / vars and add them to the __CONSOLE_VAR_DICT__
  817. def include_console(includeFile):
  818.     global __CONSOLE_VAR_DICT__ # write autoexec vars to this.
  819.     
  820.     # Execute an external py file as if local
  821.     exec(include(includeFile))
  822.  
  823. def standard_imports():
  824.     # Write local to global __CONSOLE_VAR_DICT__ for reuse,
  825.     
  826.     exec('%s%s' % ('__CONSOLE_VAR_DICT__["bpy"]=', 'bpy'))
  827.     exec('%s%s' % ('__CONSOLE_VAR_DICT__["Blender"]=', 'Blender'))
  828.     
  829.     for ls in (dir(), dir(Blender)):
  830.         for __TMP_VAR_NAME__ in ls:
  831.             # Execute the local > global coversion.
  832.             exec('%s%s' % ('__CONSOLE_VAR_DICT__[__TMP_VAR_NAME__]=', __TMP_VAR_NAME__))
  833.     
  834.     # Add dummy imports to input so output scripts to a text file work as expected
  835.     cmdBuffer.append(cmdLine('import bpy', 0, 1))
  836.     cmdBuffer.append(cmdLine('import Blender', 0, 1)) # pretend we have been executed, as we kindof have.
  837.     cmdBuffer.append(cmdLine('from Blender import *', 0, 1))
  838.  
  839. if scriptDir and console_autoexec:
  840.     include_console(console_autoexec) # pass the blender module
  841.  
  842. standard_imports() # import Blender and bpy
  843.  
  844. #-end autoexec-----------------------------------------------------------------#
  845.  
  846.  
  847. # Append new line to write to
  848. cmdBuffer.append(cmdLine(' ', 0, 0))
  849.  
  850. #------------------------------------------------------------------------------#
  851. #                    register the event handling code, GUI                     #
  852. #------------------------------------------------------------------------------#
  853. def main():
  854.     Draw.Register(draw_gui, handle_event, handle_button_event)
  855.  
  856. if __name__ == '__main__':
  857.     main()
  858.